package com.lot.ws.resource;

import static com.sun.jersey.multipart.MultiPartMediaTypes.MULTIPART_MIXED_TYPE;

import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

import javax.imageio.ImageIO;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.log4j.Logger;
import org.rosuda.REngine.REXP;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.REngineException;
import org.rosuda.REngine.Rserve.RConnection;
import org.springframework.stereotype.Component;

import sun.misc.BASE64Encoder;

import com.lot.ws.dao.MSRDao;
import com.lot.ws.dao.MSRRconnection;
import com.lot.ws.dto.AnalyticAvgInput;
import com.lot.ws.dto.AnalyticAvgOutput;
import com.lot.ws.dto.AnalyticAvgOverTypesOutput;
import com.lot.ws.dto.AvgImgDTO;
import com.lot.ws.dto.AvgImgListDTO;
import com.lot.ws.dto.CorelationDto;
import com.lot.ws.dto.DeviceComparison;
import com.lot.ws.dto.DoorMotion;
import com.lot.ws.dto.DoorMotionDetector;
import com.lot.ws.dto.MSRDevices;
import com.lot.ws.dto.MSRDoorMotionAnlysisRequest;
import com.lot.ws.dto.MSRDoorMotionAnlysisResult;
import com.lot.ws.dto.ReadingDetails;
import com.lot.ws.dto.ReadingRequest;
import com.lot.ws.exception.InvalidAttribute;
import com.lot.ws.util.DateUtil;
import com.sun.jersey.multipart.BodyPart;
import com.sun.jersey.multipart.MultiPart;

@Component
@Path("/msr")
public class MSRResource {

	private static final Logger logger = Logger.getLogger(MSRResource.class);
	// String path = "/home/azureuser/dataset.png";
	String path = "C:/Users/Chirag/Documents/dataset.png";
	//String path = "C:/dataset.png";

	@GET
	@Path("analytic/graph")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	// @Produces(com.sun.jersey.multipart.MultiPartMediaTypes.MULTIPART_MIXED)
	public Response getAvgResWithImg(
			@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("graphType") String graphType,
			@QueryParam("unit") String unit) throws REngineException,
			REXPMismatchException, IOException {

		logger.debug("\nMSRResource >> analytic");
		logger.debug("inputs:");
		logger.debug(researchId);
		logger.debug(groupId);
		logger.debug(srcId);
		logger.debug(leafType);
		logger.debug(leafValue);

		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				leafValue, leafType, DateUtil.getDate(from),
				DateUtil.getDate(to), timeScale);
		// Method that reads data from HBase - this is used by correlation
		// functionality
		double[] out = getAvg(in, unit).getAvg();
		// double[] out = {29,30,25,27,29,30,23,22};
		logger.debug("Output::" + out);
		String imgstr = "";
		if (out != null && out.length > 0) {
			File file = new File(getRGraph("main=\"Data set\", type = \""
					+ graphType + "\",xlab=\"Time\", ylab=\"" + leafValue
					+ "\"", out));
			BufferedImage img = ImageIO.read(file);
			imgstr = encodeToString(img, "jpg");
		}
		logger.debug("Image::" + imgstr);
		AvgImgListDTO outList = new AvgImgListDTO();
		List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
		AvgImgDTO temp1 = new AvgImgDTO("score", Arrays.toString(out));
		AvgImgDTO temp2 = new AvgImgDTO("image", imgstr);
		dataList.add(temp1);
		dataList.add(temp2);
		outList.setLst(dataList);
		logger.debug("dataList:::" + dataList);
		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();
	}

	@GET
	@Path("correlation")
	@Consumes(MediaType.APPLICATION_JSON)
	// @Produces(com.sun.jersey.multipart.MultiPartMediaTypes.MULTIPART_MIXED)
	@Produces(MediaType.APPLICATION_JSON)
	public Response findCorrelation(
			@QueryParam("xResearchId") String xResearchId,
			@QueryParam("xGroupId") String xGroupId,
			@QueryParam("xSrcId") String xSrcId,
			@QueryParam("xLeafValue") String xLeafValue,
			@QueryParam("xLeafType") String xLeafType,
			@QueryParam("yResearchId") String yResearchId,
			@QueryParam("yGroupId") String yGroupId,
			@QueryParam("ySrcId") String ySrcId,
			@QueryParam("yLeafValue") String yLeafValue,
			@QueryParam("yLeafType") String yLeafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("model") String model,
			@QueryParam("xunit") String xunit, @QueryParam("yunit") String yunit)
			throws InvalidAttribute {

		System.out
				.println("\n============= R Processing Correlation =============");
		logger.debug("\nx inputs:" + xResearchId + "/" + xGroupId + "/"
				+ xSrcId + "/" + xLeafType + "/" + xLeafValue);
		logger.debug("\ny inputs:" + yResearchId + "/" + yGroupId + "/"
				+ ySrcId + "/" + yLeafType + "/" + yLeafValue);

		// Get Avg for x
		AnalyticAvgInput in1 = new AnalyticAvgInput(xResearchId, xGroupId,
				xSrcId, xLeafValue, xLeafType, DateUtil.getDate(from),
				DateUtil.getDate(to), timeScale);
		Map<String, Double> map1 = getAvgAsMapWithTime(in1, xunit);
		AnalyticAvgOutput s = getAvg(in1, xunit);
		logger.info("**** " + Arrays.toString(s.getAvg()));
		// Get Avg for y
		AnalyticAvgInput in2 = new AnalyticAvgInput(yResearchId, yGroupId,
				ySrcId, yLeafValue, yLeafType, DateUtil.getDate(from),
				DateUtil.getDate(to), timeScale);

		AnalyticAvgOutput s2 = getAvg(in2, yunit);
		logger.info("**** " + Arrays.toString(s2.getAvg()));

		Map<String, Double> map2 = getAvgAsMapWithTime(in2, yunit);
		CorelationDto cor = allignTimeFrames(map1, map2);
		double[] xReadings = cor.getX();
		double[] yReadings = cor.getY();
		logger.debug("\nCorelation between ...");
		logger.info("x: " + Arrays.toString(xReadings));
		logger.info("y: " + Arrays.toString(yReadings));
		if (xReadings.length != yReadings.length) {
			throw new InvalidAttribute("x and y params",
					"unrelated co-ordinates");
		}

		RConnection c = MSRRconnection.getInstance().c;
		MultiPart multiPart = null;
		AvgImgListDTO outList = new AvgImgListDTO();
		try {
			// OK, so the device should be fine - let's plot - replace this by
			// any plotting code you desire ...
			c.assign("a", xReadings);
			c.assign("b", yReadings);
			// test
			// c.parseAndEval("a<-c(23,23,24,24,26,27,25,22,22)");
			// c.parseAndEval("b<-c(150,148,160,165,200,301,215,170,161)");
			File file = new File(getRGraph(
					"main=\"Correlation\", type=\"p\" ,xlab=\"" + xLeafValue
							+ "\", ylab=\"" + yLeafValue + "\"", xReadings,
					yReadings));
			BufferedImage img = ImageIO.read(file);
			String imgstr = encodeToString(img, "png");
			REXP sc = c.parseAndEval("cor(a,b, method = \"" + model + "\");");
			String score = sc.asString();
			logger.debug("\ncor(a,b) =" + score);
			List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
			AvgImgDTO temp1 = new AvgImgDTO("score", score);
			AvgImgDTO temp2 = new AvgImgDTO("image", imgstr);
			dataList.add(temp1);
			dataList.add(temp2);
			outList.setLst(dataList);
		} catch (REngineException e) {
			logger.debug("REngineException thrown  with input 1: " + xReadings
					+ "  input 2: " + yReadings);
			e.printStackTrace();
		} catch (REXPMismatchException e) {
			logger.debug("REXPMismatchException thrown  with input 1: "
					+ xReadings + "  input 2: " + yReadings);
			logger.debug("xReadings size:" + xReadings.length);
			logger.debug("yReadings size:" + yReadings.length);
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();
	}

	private CorelationDto allignTimeFrames(Map<String, Double> map1,
			Map<String, Double> map2) {
		int n = map1.size() < map2.size() ? map1.size() : map2.size();
		logger.debug("n= " + n);
		double[] x = new double[n];
		double[] y = new double[n];
		int i = 0;
		for (Entry<String, Double> e : map1.entrySet()) {
			if (map2.containsKey(e.getKey())) {
				x[i] = e.getValue();
				y[i] = map2.get(e.getKey());
				i++;
			}
		}
		return new CorelationDto(x, y);
	}

	private static double[] convertColToArr(Collection<Double> xReadingsCol) {
		Double[] doubleArray = (Double[]) xReadingsCol.toArray(new Double[0]);

		double[] d1 = new double[doubleArray.length];
		int i = 0;
		for (Double d : doubleArray) {
			d1[i] = d;
			i++;
		}
		return d1;
	}

	@GET
	@Path("analytic")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response getAvgRes(@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("unit") String unit) throws Exception {

		logger.debug("\nMSRResource >> analytic ");
		logger.debug("inputs:");
		logger.debug(researchId);
		logger.debug(groupId);
		logger.debug(srcId);
		logger.debug(leafType);
		logger.debug(leafValue);
		logger.debug(from);
		logger.debug(to);
		logger.debug(timeScale);

		Date dateFrom = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.ENGLISH)
				.parse(from);
		Date dateTo = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.ENGLISH)
				.parse(to);

		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				leafValue, leafType, dateFrom, dateTo, timeScale);

		// Method that reads data from hbase - this is used by corelation
		// functionality
		AnalyticAvgOutput out = getAvg(in, unit);

		return Response.status(201).header("Access-Control-Allow-Origin", "*")
				.entity(out).build();
	}

	@GET
	@Path("runscript")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response rRunScript(@QueryParam("scriptText") String scriptText) {
		System.out.println("\n============= rRunScript =============");
		RConnection c = MSRRconnection.getInstance().c;
		String buffer = "";

		// scriptText =
		// "a1<-c(@get::Ucl::home::41212::lights::deviceType::15/08/2013 18:00:00::16/08/2013 18:00:00::hourly@);\na2<-c(1,1,10,9,1,1,1,1,10,1,1);\ncor(a1,a2)";
		// scriptText =
		// "a1<-c(@get::Ucl::home::41212::lights::deviceType::15/08/2013 18:00:00::16/08/2013 18:00:00::hourly@);\na2<-c(@get::Ucl::home::41212::lights::deviceType::12/08/2013 18:00:00::13/08/2013 18:00:00::hourly@);\ncor(a1,a2)";
		System.out.println("\n" + scriptText);
		Scanner scanner = new Scanner(scriptText);

		while (scanner.hasNextLine()) {
			String line = scanner.nextLine();
			if (line.contains("@get")) {
				String stringForGet = line.substring(
						line.indexOf("@get::") + "@get::".length(),
						line.indexOf("@",
								line.indexOf("@get::") + "@get::".length()));
				String split[] = stringForGet.split("::");

				String xResearchId = split[0];
				String xGroupId = split[1];
				String xSrcId = split[2];
				String xLeafValue = split[3];
				String xLeafType = split[4];
				String from = split[5];
				String to = split[6];
				String timeScale = split[7];
				String unit = split[8];

				System.out.println("\n" + xResearchId + "/" + xGroupId + "/"
						+ xSrcId + "/" + xLeafValue + "/" + xLeafType + "/"
						+ DateUtil.getDate(from) + "/" + DateUtil.getDate(to)
						+ "/" + timeScale + "/" + unit);
				AnalyticAvgInput in1 = new AnalyticAvgInput(xResearchId,
						xGroupId, xSrcId, xLeafValue, xLeafType,
						DateUtil.getDate(from), DateUtil.getDate(to), timeScale);

				double[] readings = getAvg(in1, unit).getAvg();
				// double[] readings =
				// {12,13,14,15,17,19,20,40,100,100,100,100};

				String stringValues = Arrays.toString(readings);
				stringValues = stringValues.substring(
						stringValues.indexOf("[") + 1,
						stringValues.indexOf("]"));
				line = line.substring(0, line.indexOf("@get")) + stringValues
						+ line.substring(line.lastIndexOf("@") + 1);
			}
			try {
				REXP tempR = c.parseAndEval(line);
				buffer += line + " -> " + tempR.asString() + "\n";

			} catch (REngineException e) {
				e.printStackTrace();
			} catch (REXPMismatchException e) {
				e.printStackTrace();
			}
		}
		AvgImgListDTO outList = new AvgImgListDTO();
		List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
		AvgImgDTO temp1 = new AvgImgDTO("score", buffer);
		dataList.add(temp1);
		outList.setLst(dataList);
		logger.debug("\n" + buffer);
		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();
	}

	@GET
	@Path("anomaly")
	@Consumes(MediaType.APPLICATION_JSON)
	// @Produces(com.sun.jersey.multipart.MultiPartMediaTypes.MULTIPART_MIXED)
	@Produces(MediaType.APPLICATION_JSON)
	public Response rFindAnomaly(@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("unit") String unit) throws REngineException,
			REXPMismatchException {
		// logger.debug("============= rFindAnomaly =============");
		// RConnection c = MSRRconnection.getInstance().c;
		// String buffer = null;

		// // Get Avg for y
		// AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId,
		// srcId,
		// leafValue, leafType, DateUtil.getDate(from),
		// DateUtil.getDate(to), timeScale);
		// double[] readings = getAvg(in, unit).getAvg();
		// // double[] readings = {29,30,25,27,29,30};
		//
		// c.assign("a", readings);
		// c.parseAndEval("library(outliers)");
		// REXP temp = c.parseAndEval("grubbs.test(a, type = 10)");
		// buffer = temp.asList().at(3).asString() + "\nG = "
		// + temp.asList().at(0).asDoubles()[0] + ",U = "
		// + temp.asList().at(0).asDoubles()[1] + ", p-value = "
		// + temp.asList().at(2).asDouble()
		// + "\nAlternative hypothesis : "
		// + temp.asList().at(1).asString() + "\n";
		// logger.debug(buffer);
		//
		// File file = new File(getRGraph("main=\"Outlier\"", readings));
		//
		// MultiPart multiPart = new MultiPart().bodyPart(
		// new BodyPart(buffer, MediaType.APPLICATION_JSON_TYPE))
		// .bodyPart(new BodyPart(file, new MediaType("image", "png")));
		//
		// return Response.ok(multiPart, MULTIPART_MIXED_TYPE)
		// .header("Content-Type", "application/json").build();

		logger.debug("\n============= rFindAnomaly =============");
		RConnection c = MSRRconnection.getInstance().c;
		String buffer = "";

		// Get Avg for y
		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				leafValue, leafType, DateUtil.getDate(from),
				DateUtil.getDate(to), timeScale);
		double[] readings = getAvg(in, unit).getAvg();
		// double[] readings =
		// {29,30,25,27,29,30,1,33,34,34,34,35,36,36,37,29,33,33,33,33,33,33,100};

		// c.assign("a", readings);
		// c.parseAndEval("library(outliers)");
		// REXP temp = c.parseAndEval("grubbs.test(a, type = 10)");
		// buffer = temp.asList().at(3).asString() + "\n G = "
		// + temp.asList().at(0).asDoubles()[0] + "\n U = "
		// + temp.asList().at(0).asDoubles()[1] + "\n p-value = "
		// + temp.asList().at(2).asDouble()
		// + "\n Alternative hypothesis : "
		// + temp.asList().at(1).asString() + "\n";
		// logger.debug(buffer);

		c.assign("anomaly", readings);
		double[] result = c.eval("boxplot.stats(anomaly)$out").asDoubles();
		for (int i = 0; i < result.length; i++) {
			buffer += result[i] + " ";
		}

		String imgstr = "";
		try {
			File file = new File(getRGraphAnomaly(
					"main=\"Anomaly\", type=\"p\",xlab=\"Time\", ylab=\""
							+ leafValue + "\"", readings));
			BufferedImage img = ImageIO.read(file);
			imgstr = encodeToString(img, "png");
		} catch (Exception e) {
			logger.debug(e);
		}

		AvgImgListDTO outList = new AvgImgListDTO();
		List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
		AvgImgDTO temp1 = new AvgImgDTO("score", buffer);
		AvgImgDTO temp2 = new AvgImgDTO("image", imgstr);
		dataList.add(temp1);
		dataList.add(temp2);
		outList.setLst(dataList);

		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();

	}

	@GET
	@Path("standardDeviation")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response rFindStandardDeviation(
			@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("graphType") String graphType,
			@QueryParam("unit") String unit) throws REngineException,
			REXPMismatchException {
		// System.out
		// .println("============= rFindStandardDeviation =============");
		// RConnection c = MSRRconnection.getInstance().c;
		//
		// // Get Avg for y
		// AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId,
		// srcId,
		// leafValue, leafType, DateUtil.getDate(from),
		// DateUtil.getDate(to), timeScale);
		// double[] readings = getAvg(in, unit).getAvg();
		// // double[] readings = {1,1,10,2,1,1,1,1,1,1,1,1,1,1,1,1};
		//
		// double returnValue = standardDeviation(readings, 0);
		// logger.debug("standardDeviation = " + returnValue);
		//
		// File file = new File(getRGraph("main=\"Standard Deviation\"",
		// readings));
		//
		// MultiPart multiPart = new MultiPart().bodyPart(
		// new BodyPart("standardDeviation = " + returnValue,
		// MediaType.APPLICATION_JSON_TYPE)).bodyPart(
		// new BodyPart(file, new MediaType("image", "png")));
		//
		// return Response.ok(multiPart, MULTIPART_MIXED_TYPE)
		// .header("Content-Type", "application/json").build();

		System.out
				.println("\n============= rFindStandardDeviation =============");
		RConnection c = MSRRconnection.getInstance().c;

		// Get Avg for y
		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				leafValue, leafType, DateUtil.getDate(from),
				DateUtil.getDate(to), timeScale);
		double[] readings = getAvg(in, unit).getAvg();
		// double[] readings = {1,1,10,2,1,1,1,1,1,1,1,1,1,1,1,1};

		double returnValue = standardDeviation(readings, 0);
		logger.debug("\nstandardDeviation = " + returnValue);
		String imgstr = "";
		try {
			File file = new File(getRGraph(
					"main=\"Standard Deviation\", type = \"" + graphType
							+ "\",xlab=\"Time\", ylab=\"" + leafValue + "\"",
					readings));
			BufferedImage img = ImageIO.read(file);
			imgstr = encodeToString(img, "png");
		} catch (Exception e) {
			logger.debug(e);
		}
		AvgImgListDTO outList = new AvgImgListDTO();
		List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
		AvgImgDTO temp1 = new AvgImgDTO("score", Double.toString(returnValue));
		AvgImgDTO temp2 = new AvgImgDTO("image", imgstr);
		dataList.add(temp1);
		dataList.add(temp2);
		outList.setLst(dataList);

		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();
	}

	@GET
	@Path("standardDeviationForDeviceSet")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response rFindStandardDeviationSet(
			@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("graphType") String graphType,
			@QueryParam("unit") String unit) throws REngineException,
			REXPMismatchException {
		// System.out
		// .println("============= rFindStandardDeviation =============");
		// RConnection c = MSRRconnection.getInstance().c;
		//
		// // Get Avg for y
		// AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId,
		// srcId,
		// ".*", "deviceId", DateUtil.getDate(from), DateUtil.getDate(to),
		// timeScale);
		// double[] readings = getAvg(in, unit).getAvg();
		// // double[] readings = {1,1,10,2,1,1,1,1,1,1,1,1,1,1,1,1};
		// System.out.println(Arrays.toString(readings));
		// double returnValue = standardDeviation(readings, 0);
		// logger.debug("standardDeviation = " + returnValue);
		//
		// File file = new File(getRGraph("main=\"Standard Deviation\"",
		// readings));
		//
		// MultiPart multiPart = new MultiPart().bodyPart(
		// new BodyPart("standardDeviation = " + returnValue,
		// MediaType.APPLICATION_JSON_TYPE)).bodyPart(
		// new BodyPart(file, new MediaType("image", "png")));
		//
		// return Response.ok(multiPart, MULTIPART_MIXED_TYPE)
		// .header("Content-Type", "application/json").build();
		//

		System.out
				.println("\n============= rFindstandardDeviationForDeviceSet =============");
		RConnection c = MSRRconnection.getInstance().c;

		// Get Avg for y
		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				".*", "deviceId", DateUtil.getDate(from), DateUtil.getDate(to),
				timeScale);
		double[] readings = getAvg(in, unit).getAvg();
		// double[] readings = {1,1,10,2,1,1,1,1,1,1,1,1,1,1,1,1};

		double returnValue = standardDeviation(readings, 0);
		logger.debug("\nstandardDeviation = " + returnValue);
		String imgstr = "";
		try {
			File file = new File(getRGraph(
					"main=\"Standard Deviation\", type = \"l\",xlab=\"Time\", ylab=\""
							+ unit + "\"", readings));
			BufferedImage img = ImageIO.read(file);
			imgstr = encodeToString(img, "png");
		} catch (Exception e) {
			logger.debug(e);
		}
		AvgImgListDTO outList = new AvgImgListDTO();
		List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
		AvgImgDTO temp1 = new AvgImgDTO("score", Double.toString(returnValue));
		AvgImgDTO temp2 = new AvgImgDTO("image", imgstr);
		dataList.add(temp1);
		dataList.add(temp2);
		outList.setLst(dataList);

		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();
	}

	@GET
	@Path("boundedanomaly")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response findAnomalyWithinBoundedDataSet(
			@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale,
			@QueryParam("unit") String unit) throws REngineException,
			REXPMismatchException {

		System.out.println("============= boundedanomaly =============");
		RConnection c = MSRRconnection.getInstance().c;

		// Get Avg for y
		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				leafValue, leafType, DateUtil.getDate(from),
				DateUtil.getDate(to), timeScale);
		double[] readings = getAvg(in, unit).getAvg();

		String returnValue = getOutOfBoundVals(readings, unit);
		logger.debug("boundedanomaly = " + returnValue);
		String imgstr = "";
		try {
			File file = new File(getRGraph(
					"main=\"Anomaly(Boundary)\", type=\"p\",xlab=\"Time\", ylab=\""
							+ unit + "\"", readings));
			BufferedImage img = ImageIO.read(file);
			imgstr = encodeToString(img, "png");
		} catch (Exception e) {
			logger.debug(e);
		}
		AvgImgListDTO outList = new AvgImgListDTO();
		List<AvgImgDTO> dataList = new ArrayList<AvgImgDTO>();
		AvgImgDTO temp1 = new AvgImgDTO("anomalies ", returnValue);
		AvgImgDTO temp2 = new AvgImgDTO("image", imgstr);
		dataList.add(temp1);
		dataList.add(temp2);
		outList.setLst(dataList);

		return Response.status(200).header("Access-Control-Allow-Origin", "*")
				.entity(outList).build();
	}

	double lowBound = 0d;
	double upperBound = Double.MAX_VALUE;

	private String getOutOfBoundVals(double[] readings, String unit) {
		System.out.println("unit: " + unit);
		if (unit.equalsIgnoreCase("hrm")) {
			lowBound = 50d;
			upperBound = 110d;
		}
		String ret = "";
		for (double reading : readings) {
			if (reading < lowBound || reading > upperBound) {
				ret = ret + " " + reading;
				System.out.println(reading);
			}
		}
		return ret;
	}

	@GET
	@Path("devices")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getAppsForCategory() {
		logger.debug("MSRResource >> devices ");

		MSRDevices col = new MSRDevices();
		List<DeviceComparison> lst = new ArrayList<DeviceComparison>();

		DeviceComparison dev1 = new DeviceComparison("Lighting", 227.03f,
				"April 1, 2012");
		DeviceComparison dev2 = new DeviceComparison("Furnace", 34.98f,
				"April 1, 2012");
		DeviceComparison dev3 = new DeviceComparison("Air Conditioning",
				10.87f, "April 1, 2012");
		DeviceComparison dev4 = new DeviceComparison("Smoke Alarms", 6.01f,
				"April 1, 2012");
		DeviceComparison dev5 = new DeviceComparison("Television", 59.02f,
				"April 1, 2012");
		DeviceComparison dev6 = new DeviceComparison("Microsave", 01.03f,
				"April 1, 2012");
		lst.add(dev1);
		lst.add(dev2);
		lst.add(dev3);
		lst.add(dev4);
		lst.add(dev5);
		lst.add(dev6);
		col.setDeviceDetails(lst);
		// return appsCol;
		return Response.status(201).header("Access-Control-Allow-Origin", "*")
				.entity(col).build();
	}

	@GET
	@Path("door")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getDoorMotion() {
		logger.debug("MSRResource >> door motion ");

		MSRDoorMotionAnlysisResult col = new MSRDoorMotionAnlysisResult();
		List<DoorMotion> lst = new ArrayList<DoorMotion>();
		DoorMotion dev1 = new DoorMotion(2, 10, "12");
		DoorMotion dev2 = new DoorMotion(32, 0, "13");
		DoorMotion dev3 = new DoorMotion(1, 1, "14");
		DoorMotion dev4 = new DoorMotion(1, 10, "15");
		DoorMotion dev5 = new DoorMotion(8, 20, "15");

		lst.add(dev1);
		lst.add(dev2);
		lst.add(dev3);
		lst.add(dev4);
		lst.add(dev5);
		col.setDoorMotionResults(lst);
		// return appsCol;
		return Response.status(201).header("Access-Control-Allow-Origin", "*")
				.entity(col).build();
	}

	@POST
	@Path("door")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	public Response StoreInDB(MSRDoorMotionAnlysisRequest req)
			throws InvalidAttribute {
		logger.debug("******************* "
				+ "Request recieved to write data : " + req
				+ " ******************* ");
		// validate request
		File file = new File("door_data.txt");

		// if file doesnt exists, then create it
		if (!file.exists()) {
			try {
				file.createNewFile();
				logger.debug("Creates new file!!!!!!");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		try {
			for (DoorMotionDetector detector : req.getDoorMotion()) {

				String content = detector.getRecordTime() + '\t'
						+ detector.getLocation() + '\t' + detector.getStatus();
				logger.debug("Writing to file --------" + content);
				// MSR1Util.writeToFS(detector.getRecordTime() + '\t' +
				// detector.getLocation() + '\t' + detector.getStatus());
				FileWriter fw = new FileWriter(file, true);
				// fw = new FileWriter(file.getName());
				BufferedWriter bw = new BufferedWriter(fw);
				bw.append(content);
				bw.newLine();
				bw.close();
			}

		} catch (IOException e) {
			e.printStackTrace();
		}

		return Response.status(201).header("Access-Control-Allow-Origin", "*")
				.entity(req).build();
	}

	@POST
	@Path("reading/submit")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	public Response storeInHbase(ReadingRequest req) throws InvalidAttribute {
		logger.debug("******************* "
				+ "Request recieved to write data : " + req
				+ " ******************* ");
		try {
			for (ReadingDetails reading : req.getReadings()) {
				String pkey = reading.getResearchId() + "/"
						+ reading.getGroupId() + "/" + reading.getSrcId() + "/"
						+ reading.getDeviceId();
				// + "/" + reading.getRecordTime();

				Date rTime = DateUtil.getRecordingDate(reading.getRecordTime());
				String rTimeStr = DateUtil.makeFixedLength(String.valueOf(rTime
						.getTime()));
				logger.debug("Adding to hbase..");

				String details = buildDetails(reading);

				logger.debug("Adding to HBASE... ");
				logger.debug("rowkey:" + pkey + " timestamp(Qualifier):"
						+ rTimeStr + " data:" + details.toString());

				MSRDao.addRecord(pkey, "details", rTimeStr, details.toString());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return Response.status(201).header("Access-Control-Allow-Origin", "*")
				.entity(req).build();
	}

	@GET
	@Path("analytic/types")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response getAvgResOverTypes(
			@QueryParam("researchId") String researchId,
			@QueryParam("groupId") String groupId,
			@QueryParam("srcId") String srcId,
			@QueryParam("leafValue") String leafValue,
			@QueryParam("leafType") String leafType,
			@QueryParam("from") String from, @QueryParam("to") String to,
			@QueryParam("timeScale") String timeScale) throws Exception {

		logger.debug("MSRResource >> analytic >> graph");

		logger.debug("inputs:");
		logger.debug(researchId);
		logger.debug(groupId);
		logger.debug(srcId);
		// logger.debug(leafType);
		// logger.debug(leafValue);
		logger.debug(from);
		logger.debug(to);
		logger.debug(timeScale);

		Date dateFrom = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.ENGLISH)
				.parse(from);
		Date dateTo = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.ENGLISH)
				.parse(to);

		AnalyticAvgInput in = new AnalyticAvgInput(researchId, groupId, srcId,
				leafValue, leafType, dateFrom, dateTo, timeScale);

		// Method that reads data from hbase - this is used by corelation
		// functionality
		Map avgs = getAvgOverAllTypes(in);
		AnalyticAvgOverTypesOutput out = new AnalyticAvgOverTypesOutput();
		out.setAvgs(avgs);

		return Response.status(201).header("Access-Control-Allow-Origin", "*")
				.entity(out).build();
	}

	public static double standardDeviation(double[] array, int option) {
		if (array.length < 2)
			return Double.NaN;

		double sum = 0.0;
		double sd = 0.0;
		double diff;
		double meanValue = mean(array);

		for (int i = 0; i < array.length; i++) {
			diff = array[i] - meanValue;
			sum += diff * diff;
		}
		sd = Math.sqrt(sum / (array.length - option));

		return sd;
	}

	public static void main(String a[]) {
		double[] r1 = { 0d, 0d, 0d, 0d, 0d };
		double[] r2 = { 5d, 5d, 5d, 5d, 5d };

		System.out.println(standardDeviation(r1, 0));
		System.out.println(standardDeviation(r2, 0));

	}

	public static double mean(double[] array) {
		double sum = 0.0;

		for (int i = 0; i < array.length; i++)
			sum += array[i];

		return sum / array.length;
	}

	public static void maina(String[] agrs) {

		try {
			Date dateFrom = new SimpleDateFormat("dd/MM/yyyy HH:mm",
					Locale.ENGLISH).parse("18/08/2013 00:01");
			Date dateTo = new SimpleDateFormat("dd/MM/yyyy HH:mm",
					Locale.ENGLISH).parse("22/08/2013 00:01");
			AnalyticAvgInput in = new AnalyticAvgInput("Ucl", "home", "12343",
					"lights", "deviceType", dateFrom, dateTo, "hourly");

			AnalyticAvgOutput out = getAvg(in, "e");

			logger.debug("Printing the result : " + out);
		} catch (ParseException e) {
			e.printStackTrace();
		}

	}

	private static AnalyticAvgOutput getAvg(AnalyticAvgInput input, String unit) {

		AnalyticAvgOutput out = new AnalyticAvgOutput();
		String rowKey = generateRowKey(input, unit);

		logger.info("Generated rowKey : " + rowKey);
		Collection<Double> avgs = null;
		try {
			avgs = MSRDao.getReadingAsMapWithTime(rowKey, input).values();
			logger.info("avgs::" + avgs);
		} catch (Exception e) {
			e.printStackTrace();
		}
		double[] d1 = convertColToArr(avgs);

		out.setAvg(d1);
		return out;

	}

	private static AnalyticAvgOutput getAvgCombinedSetForType(
			AnalyticAvgInput input, String unit) {

		AnalyticAvgOutput out = new AnalyticAvgOutput();

		AnalyticAvgInput input1 = input;

		String rowKey = generateRowKey(input, unit);

		Collection<Double> avgs = null;
		try {
			avgs = MSRDao.getReadingAsMapWithTime(rowKey, input).values();
			logger.debug("######################### avgs::" + avgs);
		} catch (Exception e) {
			e.printStackTrace();
		}
		double[] d1 = convertColToArr(avgs);

		out.setAvg(d1);
		return out;

	}

	private static Map<String, Double> getAvgAsMapWithTime(
			AnalyticAvgInput input, String unit) {

		String rowKey = generateRowKey(input, unit);
		logger.debug("rowKey : " + rowKey);
		Map<String, Double> avgs = null;
		try {
			avgs = MSRDao.getReadingAsMapWithTime(rowKey, input);
			logger.debug("######################### avgs::" + avgs);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// Converting Double array into the primitive type double array
		return avgs;

	}

	private static String generateRowKey(AnalyticAvgInput input, String unit) {
		StringBuilder strBuilder = new StringBuilder();
		if (input.getResearchId() != null && !input.getResearchId().equals(""))
			strBuilder.append("research");
		strBuilder.append("/");
		if (input.getGroupId() != null && !input.getGroupId().equals(""))
			strBuilder.append("group");
		strBuilder.append("/");
		if (input.getSrcId() != null && !input.getSrcId().equals(""))
			strBuilder.append("source");
		strBuilder.append("/");
		// check if leafType value is null
		if (input.getLeafType() != null && !input.getLeafType().equals(""))
			strBuilder.append(input.getLeafType());
		strBuilder.append("-");
		// check if ResearchId value is null
		if (input.getResearchId() != null && !input.getResearchId().equals(""))
			strBuilder.append(input.getResearchId());
		strBuilder.append("/");
		// check if GroupId value is null
		if (input.getGroupId() != null && !input.getGroupId().equals(""))
			strBuilder.append(input.getGroupId());
		strBuilder.append("/");
		// check if SourceID value is null
		if (input.getSrcId() != null && !input.getSrcId().equals(""))
			strBuilder.append(input.getSrcId());
		strBuilder.append("/");
		// check if LeafValue value is null
		if (input.getLeafValue() != null && !input.getLeafValue().equals(""))
			strBuilder.append(input.getLeafValue());
		if (unit != null && !unit.equals(""))
			strBuilder.append("_").append(unit);
		// else
		// strBuilder.append("_").append("e");
		// convert Builder string to a string
		String rowKey = strBuilder.toString();
		return rowKey;
	}

	private static Map getAvgOverAllTypes(AnalyticAvgInput input) {

		AnalyticAvgOutput out = new AnalyticAvgOutput();

		String deviceTypes[] = { "lights", "heating", "furnace", "door" };

		Map deviceAvg = new HashMap();
		for (String typ : deviceTypes) {

			StringBuilder strBuilder = new StringBuilder();
			strBuilder.append("research");
			strBuilder.append("/");
			strBuilder.append("group");
			strBuilder.append("/");
			strBuilder.append("source");
			strBuilder.append("/");
			// check if leafType value is null
			strBuilder.append("deviceType");
			strBuilder.append("-");
			// check if ResearchId value is null
			if (input.getResearchId() != null
					&& !input.getResearchId().equals(""))
				strBuilder.append(input.getResearchId());
			else
				strBuilder.append(".*");
			strBuilder.append("/");
			// check if GroupId value is null
			if (input.getGroupId() != null && !input.getGroupId().equals(""))
				strBuilder.append(input.getGroupId());
			else
				strBuilder.append(".*");
			strBuilder.append("/");
			// check if SourceID value is null
			if (input.getSrcId() != null && !input.getSrcId().equals(""))
				strBuilder.append(input.getSrcId());
			else
				strBuilder.append(".*");

			strBuilder.append("/");
			// check if LeafValue value is null
			strBuilder.append(typ);

			input.setTimeScale("daily");

			// convert Builder string to a string
			String rowKey = strBuilder.toString();
			logger.debug(" ** rowKey=" + rowKey);
			logger.debug(" ** typ=" + typ);
			Collection<Double> avgs = null;
			try {
				avgs = MSRDao.getReadingAsMapWithTime(rowKey, input).values();
				logger.debug("######################### avgs::" + avgs);
			} catch (Exception e) {
				e.printStackTrace();
			}
			double d = getAvgOfArr(avgs);
			deviceAvg.put(typ, d);
		}
		return deviceAvg;

	}

	private static double getAvgOfArr(Collection<Double> avgs) {
		double avg = 0d;
		for (Double a : avgs)
			avg += a;
		return avg / avgs.size();
	}

	public String getRGraph(String option, double[]... dataset)
			throws REngineException, REXPMismatchException {
		logger.debug("\n============= get R Graph =============");

		String device = "png"; // device we'll call (this would work with pretty
								// much any bitmap device)
		RConnection c = MSRRconnection.getInstance().c;

		REXP xp = c.parseAndEval(device + "('" + path + "')");

		if (xp.inherits("try-error")) { // if the result is of the class
										// try-error then there was a problem
			System.err.println("\nCan't open " + device + " graphics device:\n"
					+ xp.asString());
			// this is analogous to 'warnings', but for us it's sufficient to
			// get just the 1st warning
			REXP w = c
					.eval("if (exists('last.warning') && length(last.warning)>0) names(last.warning)[1] else 0");
			if (w.isString())
				System.err.println(w.asString());
		}

		if (dataset.length == 1) {
			if (dataset[0].length == 0) {
				logger.debug("\n============= No data =============");
			}
			c.assign("y", dataset[0]);
			c.parseAndEval("plot(y," + option + ");");
		} else if (dataset.length == 2) {
			if (dataset[0].length == 0 || dataset[1].length == 0) {
				logger.debug("\n============= No data =============");
			}
			c.assign("x", dataset[0]);
			c.assign("y", dataset[1]);
			c.parseAndEval("plot(x,y," + option + ");");
		}

		c.parseAndEval("dev.off();");

		// sam's mc String path = "/private/tmp/Rserv/conn3061/dataset.png";

		return path;
	}

	public String getRGraphAnomaly(String option, double[]... dataset)
			throws REngineException, REXPMismatchException {
		logger.debug("\n============= get R Graph =============");

		String device = "png"; // device we'll call (this would work with pretty
								// much any bitmap device)
		RConnection c = MSRRconnection.getInstance().c;

		REXP xp = c.parseAndEval(device + "('" + path + "')");

		if (xp.inherits("try-error")) { // if the result is of the class
										// try-error then there was a problem
			System.err.println("\nCan't open " + device + " graphics device:\n"
					+ xp.asString());

			REXP w = c
					.eval("if (exists('last.warning') && length(last.warning)>0) names(last.warning)[1] else 0");
			if (w.isString())
				System.err.println(w.asString());
		}

		if (dataset.length == 1) {
			if (dataset[0].length == 0) {
				logger.debug("\n============= No data =============");
			}
			c.assign("y", dataset[0]);
			c.parseAndEval("y.out <- which(y %in% boxplot.stats(y)$out);");
			c.parseAndEval("plot(y," + option + ");");
			c.parseAndEval("points(y.out, y[y.out],col=\"red\", pch=\"+\", cex=2)");

		} else if (dataset.length == 2) {
			c.assign("x", dataset[0]);
			c.assign("y", dataset[1]);
			c.parseAndEval("df <- data.frame(x, y);");
			c.parseAndEval("attach(df);");
			c.parseAndEval("y.out <- which(y %in% boxplot.stats(y)$out);");
			c.parseAndEval("b <- which(y %in% boxplot.stats(y)$out);");
			c.parseAndEval("(outlier.list1 <- intersect(a,b))");
			c.parseAndEval("plot(df," + option + ");");
			c.parseAndEval("points(df[outlier.list1,], col=\"red\", pch=\"+\", cex=2)");
		}

		c.parseAndEval("dev.off();");

		// sam's mc String path = "/private/tmp/Rserv/conn3061/dataset.png";

		return path;
	}

	public static String encodeToString(BufferedImage image, String type) {
		String imageString = null;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		try {
			ImageIO.write(image, type, bos);
			byte[] imageBytes = bos.toByteArray();

			BASE64Encoder encoder = new BASE64Encoder();
			imageString = encoder.encode(imageBytes);

			bos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return imageString;
	}

	public static String buildDetails(ReadingDetails reading) {

		Date rTime = DateUtil.getRecordingDate(reading.getRecordTime());

		StringBuilder details = new StringBuilder();
		details.append(reading.getResearchId());
		details.append(",");
		details.append(reading.getGroupId());
		details.append(",");
		details.append(reading.getSrcId());
		details.append(",");
		details.append(reading.getDeviceId());
		details.append(",");
		details.append(reading.getDeviceType());
		details.append(",");
		details.append(reading.getLocation());
		details.append(",");
		details.append(reading.getValue());
		details.append(",");
		details.append(rTime.toString());
		return details.toString();
	}
}
